﻿/*************************************************************************************
 * 文 件 名:   HK_HierarchyPro
 * 
 * 描    述:   Hierarchy视窗的拓展，可以在Setting里面关闭或开启
 * 
 * 版    本：  V1.0
 * 创 建 者：  京产肠饭
 * 创建时间：  2021/11/29
 * 链   接：  https://gitee.com/JingChanChangFan/hk_-unity-tools
*************************************************************************************/

using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
using UnityEditor;

[InitializeOnLoad]
public class HK_HierarchyPro
{
    // 静态构造函数，用于 InitializeOnLoad 的时候调用
    static HK_HierarchyPro()
    {
        HK_ToolSetting.ChangeHierarchyEvent += OnChangeHierarchyEvent;

        EditorApplication.hierarchyWindowItemOnGUI += ComponentsShow;
        EditorApplication.hierarchyWindowItemOnGUI += TagAndLayerShow;
        EditorApplication.hierarchyWindowItemOnGUI += ToggleShow;
    }

    static void OnChangeHierarchyEvent(bool _isEnable)
    {
        isEnable = _isEnable;
    }

    static bool enable = true;
    static bool isEnable
    {
        get
        {
            return enable;
        }
        set
        {
            if (value == true)
            {
                EditorApplication.hierarchyWindowItemOnGUI += ComponentsShow;
                EditorApplication.hierarchyWindowItemOnGUI += TagAndLayerShow;
                EditorApplication.hierarchyWindowItemOnGUI += ToggleShow;

                enable = value;
            }
            else
            {
                EditorApplication.hierarchyWindowItemOnGUI -= ComponentsShow;
                EditorApplication.hierarchyWindowItemOnGUI -= TagAndLayerShow;
                EditorApplication.hierarchyWindowItemOnGUI -= ToggleShow;

                enable = value;
            }
        }
    }

    /*最大显示的图标数量*/
    static int maxIconNum = 3;

    /*当前需要便宜 组件 显示的 物体TF*/
    static Transform nowOffsetTF = null;
    static int offsetNum = 0;

    // 想要显示的 组件 列表
    static List<System.Type> showTypes = new List<System.Type>(){
        typeof(Camera),
        typeof(Collider),
        typeof(AudioSource),
        typeof(AudioListener),
    };

    // 显示物体的 tag 和 layer 
    static void TagAndLayerShow(int instanceid, Rect rect)
    {
        GameObject go = EditorUtility.InstanceIDToObject(instanceid) as GameObject;

        //剔除最上面的scene 物体
        if (!go)
            return;

        Rect newRect = new Rect(rect);

        if (rect.width > 280)
        {
            newRect.x = newRect.width + newRect.x - 120;
            newRect.y += 2;
            var myStyle = new GUIStyle();
            myStyle.normal.textColor = Color.gray;

            string layerStr = LayerMask.LayerToName(go.layer);

            if (layerStr != "Default")
            {
                GUI.Label(newRect, '[' + layerStr + ']', myStyle);
            }
        }
    }

    // 显示最右侧的激活按钮
    static void ToggleShow(int instanceid, Rect rect)
    {
        GameObject go = EditorUtility.InstanceIDToObject(instanceid) as GameObject;
        //剔除最上面的scene 物体
        if (!go)
        {
            return;
        }

        /*----------------------------Toggle 显示----------------------*/

        Rect newRect = new Rect(rect);
        newRect.x = newRect.width + newRect.x - 15;
        newRect.y += 2;

        GUI.changed = false;
        GUI.Toggle(newRect, go.activeSelf, "");

        if (GUI.changed)
            go.SetActive(!go.activeSelf);

        /*----------------------------Toggle 显示----------------------*/
    }

    // 显示 特殊的组件列表
    static void ComponentsShow(int instanceid, Rect rect)
    {
        GameObject go = EditorUtility.InstanceIDToObject(instanceid) as GameObject;

        //剔除最上面的scene 物体
        if (!go)
            return;

        //求出当前Hierarchy的总宽度
        rect.width += rect.x;
        //取得当前物体所有的 Component
        List<Component> coms = new List<Component>(go.GetComponents<Component>());
        //遍历coms中的组件类型，如果有不显示的，就移除掉
        for (int i = 0; i < coms.Count; i++)
        {
            if (!coms[i])
            {
                continue;
            }
            //剔除不需要显示的 组件
            if (TypeCheck(coms[i].GetType()))
            {
                coms.RemoveAt(i);
                i--;
            }
        }
        //图标的大小
        int iconSize = 16;

        //如果当前物体是 需要偏移的物体  offset 为 offsetNum 否则为 0
        int offset = go.transform == nowOffsetTF ? offset = offsetNum : 0;
        //如果当前物体的组件数量 <=  maxIconNum  把nowOffset 和 offsetNum 
        if (coms.Count <= maxIconNum && go.transform == nowOffsetTF)
        {
            nowOffsetTF = null;
            offsetNum = 0;
        }
        if (coms.Count > maxIconNum)
        {
            GUIStyle style = new GUIStyle(GUI.skin.label);
            style.fontSize = 9;
            style.alignment = TextAnchor.MiddleCenter;

            if (GUI.Button(new Rect(rect.width - (iconSize + 1.5f) * (maxIconNum + 2), rect.y + 1, 17, 17), "•••", style))
            {
                if (nowOffsetTF != go.transform)
                {
                    nowOffsetTF = go.transform;
                    offsetNum = 0;
                }
                offsetNum += maxIconNum;
                if (offsetNum >= coms.Count)
                {
                    offsetNum = 0;
                }
            }
        }

        for (int i = 0; i + offset < coms.Count && i < maxIconNum; i++)
        {
            Component com = coms[i + offset];
            PropertyInfo propertyInfo;
            try
            {
                propertyInfo = com.GetType().GetProperty("enabled");
            }
            catch
            {
                continue;
            }

            if ((bool)propertyInfo.GetGetMethod().Invoke(com, null))
            {
                GUI.color = Color.white;
            }
            else
            {
                GUI.color = Color.gray;
            }

            //根据组件类型返回 组件的小图标
            Texture2D comTexture = AssetPreview.GetMiniThumbnail(com);
            if (comTexture)
            {
                GUIStyle style = new GUIStyle();
                style.normal.textColor = Color.white;

                if (GUI.Button(new Rect(rect.width - (iconSize + 1) * (i + 2), rect.y + 1, iconSize, iconSize), comTexture, style))
                {
                    bool enabled = (bool)propertyInfo.GetGetMethod().Invoke(com, null);

                    Undo.RecordObject(com, enabled ? "Disable Component" : "Enable Component");
                    propertyInfo.GetSetMethod().Invoke(com, new object[] { !enabled });
                }
                GUI.color = Color.white;
            }
        }
    }

    // 检查当前组件类型 是否是 不显示类型
    static bool TypeCheck(System.Type type)
    {
        for (int i = 0; i < showTypes.Count; i++)
        {
            if (type == showTypes[i] || type.IsSubclassOf(showTypes[i]))
            {
                return false;
            }
        }
        return true;
    }
}
